﻿// Copyright (c)  YISH. All rights reserved.
// Licensed under the MIT License, See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using NSQL.Builder;
using NSQL.Expressions;
using NSQL.Expressions.Functions;
using NSQL.Statements;

namespace NSQL
{
    [SuppressMessage("ReSharper", "UnusedMember.Global")]
    public static class Prelude
    {
        public static Table Table(string tableName) => tableName;
        public static ColumnExpression Column(Identifier columnName) => columnName;
        public static ColumnExpression Column(string columnName) => columnName;
        public static ColumnExpression Column(int columnId) => columnId;
        public static IntegerExpression Constant(int value) => value;
        public static NumericExpression Constant(double value) => value;
        public static StringExpression Constant(string value) => value;
        public static DateTimeExpression Constant(DateTime value) => value;

        public static NowFunction Now() => NowFunction.Instance;

        public static ToCharFunction ToChar(IExpression expression, IStringExpression formatter)
        {
            return new ToCharFunction(expression, formatter);
        }

        public static ToCharFunction ToChar(IExpression expression, StringExpression formatter)
        {
            return new ToCharFunction(expression, formatter);
        }

        public static FormatFunction Format(IDateTimeExpression dateTime, StringExpression format)
        {
            return new FormatFunction(dateTime, format);
        }
        
        public static FormatFunction Format(StringExpression format, IExpression[] expressions)
        {
            return new FormatFunction(format, expressions);
        }

        public static ToNumberFunction ToNumber(IExpression expression, IStringExpression formatter)
        {
            return new ToNumberFunction(expression, formatter);
        }

        public static ToNumberFunction ToNumber(IExpression expression, StringExpression formatter)
        {
            return new ToNumberFunction(expression, formatter);
        }

        public static ToNumberFunction ToNumber(IExpression expression)
        {
            return new ToNumberFunction(expression);
        }

        public static ToNumberFunction ToNumber(StringExpression expression, StringExpression formatter)
        {
            return new ToNumberFunction(expression, formatter);
        }

        public static ToNumberFunction ToNumber(StringExpression expression, IStringExpression formatter)
        {
            return new ToNumberFunction(expression, formatter);
        }

        public static ToNumberFunction ToNumber(StringExpression expression)
        {
            return new ToNumberFunction(expression);
        }

        public static PercentileFunction Percentile(INumericExpression expression, INumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Cont, order);
        }

        public static PercentileFunction Percentile(INumericExpression expression, NumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Cont, order);
        }

        public static PercentileFunction PercentileCont(INumericExpression expression, INumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Cont, order);
        }

        public static PercentileFunction PercentileCont(INumericExpression expression, NumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Cont, order);
        }

        public static PercentileFunction PercentileDisc(INumericExpression expression, INumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Disc, order);
        }

        public static PercentileFunction PercentileDisc(INumericExpression expression, NumericExpression p,
            Order order = Order.None)
        {
            return new PercentileFunction(expression, p, PercentileType.Disc, order);
        }


        public static MedianFunction Median(INumericExpression expression)
        {
            return new MedianFunction(expression);
        }


        public static SumFunction Sum(IExpression expression, AggregateType type = AggregateType.None)
        {
            return new SumFunction(expression, type);
        }

        public static SumFunction Sum(ColumnExpression expression, AggregateType type = AggregateType.None)
        {
            return new SumFunction(expression, type);
        }

        public static StdDevFunction StdDev(IExpression expression)
        {
            return new StdDevFunction(expression);
        }

        public static StdDevFunction StdDev(ColumnExpression expression)
        {
            return new StdDevFunction(expression);
        }

        public static VarianceFunction Variance(IExpression expression)
        {
            return new VarianceFunction(expression);
        }

        public static VarianceFunction Variance(ColumnExpression expression)
        {
            return new VarianceFunction(expression);
        }

        public static AvgFunction Avg(IExpression expression, AggregateType type = AggregateType.None)
        {
            return new AvgFunction(expression, type);
        }

        public static AvgFunction Avg(ColumnExpression expression, AggregateType type = AggregateType.None)
        {
            return new AvgFunction(expression, type);
        }

        public static MaxFunction Max(IExpression expression, AggregateType type = AggregateType.None)
        {
            return new MaxFunction(expression, type);
        }

        public static MaxFunction Max(ColumnExpression expression, AggregateType type = AggregateType.None)
        {
            return new MaxFunction(expression, type);
        }

        public static MinFunction Min(IExpression expression, AggregateType type = AggregateType.None)
        {
            return new MinFunction(expression, type);
        }

        public static MinFunction Min(ColumnExpression expression, AggregateType type = AggregateType.None)
        {
            return new MinFunction(expression, type);
        }

        public static CountFunction Count(IExpression expression, AggregateType type = AggregateType.None)
        {
            return new CountFunction(expression, type);
        }

        public static CountFunction Count(ColumnExpression expression, AggregateType type = AggregateType.None)
        {
            return new CountFunction(expression, type);
        }

        public static RoundFunction Round(IExpression expression, IIntegerExpression decimals)
        {
            return new RoundFunction(expression, decimals);
        }

        public static RoundFunction Round(ColumnExpression expression)
        {
            return Round(expression, 0);
        }

        public static RoundFunction Round(ColumnExpression expression, IntegerExpression decimals)
        {
            return new RoundFunction(expression, decimals);
        }

        public static CeilFunction Ceil(INumericExpression number)
        {
            return new CeilFunction(number);
        }

        public static CeilFunction Ceil(NumericExpression number)
        {
            return new CeilFunction(number);
        }

        public static CeilFunction Ceil(ColumnExpression number)
        {
            return new CeilFunction(number);
        }

        public static FloorFunction Floor(INumericExpression number)
        {
            return new FloorFunction(number);
        }

        public static FloorFunction Floor(NumericExpression number)
        {
            return new FloorFunction(number);
        }

        public static FloorFunction Floor(ColumnExpression number)
        {
            return new FloorFunction(number);
        }

        public static CastFunction Cast(IExpression expression, string targetType)
        {
            return new CastFunction(expression, targetType);
        }

        /// <summary>
        /// The Extract() function extracts parts from a date
        /// </summary>
        public static ExtractFunction Extract(IExpression dateTime, ExtractType extractType)
        {
            return new ExtractFunction(dateTime, extractType);
        }

        public static SinFunction Sin(INumericExpression number)
        {
            return new SinFunction(number);
        }

        public static SinFunction Sin(NumericExpression number)
        {
            return new SinFunction(number);
        }

        public static CosFunction Cos(INumericExpression number)
        {
            return new CosFunction(number);
        }

        public static CosFunction Cos(NumericExpression number)
        {
            return new CosFunction(number);
        }

        public static TanFunction Tan(INumericExpression number)
        {
            return new TanFunction(number);
        }

        public static TanFunction Tan(NumericExpression number)
        {
            return new TanFunction(number);
        }

        public static AbsFunction Abs(INumericExpression number)
        {
            return new AbsFunction(number);
        }

        public static AbsFunction Abs(NumericExpression number)
        {
            return new AbsFunction(number);
        }

        public static SignFunction Sign(INumericExpression number)
        {
            return new SignFunction(number);
        }

        public static SignFunction Sign(NumericExpression number)
        {
            return new SignFunction(number);
        }

        public static PowFunction Pow(INumericExpression x, INumericExpression y)
        {
            return new PowFunction(x, y);
        }

        public static PowFunction Pow(NumericExpression x, NumericExpression y)
        {
            return new PowFunction(x, y);
        }

        public static PowFunction Power(INumericExpression x, INumericExpression y)
        {
            return new PowFunction(x, y);
        }

        public static PowFunction Power(NumericExpression x, NumericExpression y)
        {
            return new PowFunction(x, y);
        }

        public static SqrtFunction Sqrt(NumericExpression expression)
        {
            return new SqrtFunction(expression);
        }

        public static SqrtFunction Sqrt(INumericExpression expression)
        {
            return new SqrtFunction(expression);
        }

        public static LogFunction Log10(NumericExpression number)
        {
            return new LogFunction((IntegerExpression) 10, number);
        }

        public static LogFunction Log10(INumericExpression number)
        {
            return new LogFunction((IntegerExpression) 10, number);
        }

        public static LogFunction Log(NumericExpression number)
        {
            return new LogFunction(number);
        }

        public static LogFunction Log(INumericExpression number)
        {
            return new LogFunction(number);
        }

        public static LogFunction Log(NumericExpression @base, NumericExpression number)
        {
            return new LogFunction(@base, number);
        }

        public static LogFunction Log(NumericExpression @base, INumericExpression number)
        {
            return new LogFunction(@base, number);
        }

        public static LogFunction Log(INumericExpression @base, NumericExpression number)
        {
            return new LogFunction(@base, number);
        }

        public static LogFunction Log(INumericExpression @base, INumericExpression number)
        {
            return new LogFunction(@base, number);
        }

        public static RankFunction Rank(ColumnExpression number, Order order = Order.None)
        {
            return new RankFunction(number, order);
        }

        public static RankFunction Rank(INumericExpression number, Order order = Order.None)
        {
            return new RankFunction(number, order);
        }

        public static RankFunction DenseRank(ColumnExpression number, Order order = Order.None)
        {
            return new RankFunction(number, true, order);
        }

        public static RankFunction DenseRank(INumericExpression number, Order order = Order.None)
        {
            return new RankFunction(number, true, order);
        }

        public static PadFunction PadLeft(IStringExpression text, IntegerExpression length,
            StringExpression paddingText = null)
        {
            return new PadFunction(text, PaddingType.Left, length, paddingText);
        }

        public static PadFunction PadLeft(StringExpression text, IntegerExpression length,
            StringExpression paddingText = null)
        {
            return new PadFunction(text, PaddingType.Left, length, paddingText);
        }

        public static PadFunction PadRight(IStringExpression text, IntegerExpression length,
            StringExpression paddingText = null)
        {
            return new PadFunction(text, PaddingType.Right, length, paddingText);
        }

        public static PadFunction PadRight(StringExpression text, IntegerExpression length,
            StringExpression paddingText = null)
        {
            return new PadFunction(text, PaddingType.Right, length, paddingText);
        }

        public static TrimFunction Trim(StringExpression text)
        {
            return new TrimFunction(text);
        }

        public static TrimFunction Trim(IStringExpression text)
        {
            return new TrimFunction(text);
        }

        public static TrimFunction Trim(IStringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText);
        }

        public static TrimFunction Trim(StringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText);
        }

        public static TrimFunction TrimStart(StringExpression text)
        {
            return new TrimFunction(text, null, TrimType.Leading);
        }

        public static TrimFunction TrimStart(IStringExpression text)
        {
            return new TrimFunction(text, null, TrimType.Leading);
        }

        public static TrimFunction TrimStart(IStringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText, TrimType.Leading);
        }

        public static TrimFunction TrimStart(StringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText, TrimType.Leading);
        }

        public static TrimFunction TrimEnd(IStringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText, TrimType.Trailing);
        }

        public static TrimFunction TrimEnd(StringExpression text, StringExpression trimText)
        {
            return new TrimFunction(text, trimText, TrimType.Trailing);
        }

        public static LowerFunction Lower(IStringExpression text)
        {
            return new LowerFunction(text);
        }

        public static LowerFunction Lower(StringExpression text)
        {
            return new LowerFunction(text);
        }

        public static UpperFunction Upper(IStringExpression text)
        {
            return new UpperFunction(text);
        }

        public static UpperFunction Upper(StringExpression text)
        {
            return new UpperFunction(text);
        }

        public static ReplaceFunction Replace(IStringExpression text, StringExpression oldText,
            StringExpression newText)
        {
            return new ReplaceFunction(text, oldText, newText);
        }

        public static ReplaceFunction Replace(StringExpression expression, StringExpression oldText,
            StringExpression newText)
        {
            return new ReplaceFunction(expression, oldText, newText);
        }

        public static ReverseFunction Reverse(IStringExpression text)
        {
            return new ReverseFunction(text);
        }

        public static ReverseFunction Reverse(StringExpression text)
        {
            return new ReverseFunction(text);
        }

        public static LengthFunction Length(StringExpression text)
        {
            return new LengthFunction(text);
        }

        public static LengthFunction Length(IStringExpression text)
        {
            return new LengthFunction(text);
        }

        public static SubStringFunction SubString(IStringExpression text, IntegerExpression position,
            IntegerExpression length)
        {
            return new SubStringFunction(text, position, length);
        }

        public static SubStringFunction SubString(IStringExpression text, IntegerExpression position)
        {
            return new SubStringFunction(text, position);
        }

        public static SubStringFunction SubString(StringExpression text, IntegerExpression position,
            IntegerExpression length)
        {
            return new SubStringFunction(text, position, length);
        }

        public static SubStringFunction SubString(StringExpression text, IntegerExpression position)
        {
            return new SubStringFunction(text, position);
        }

        public static LeftFunction Left(IStringExpression text, IntegerExpression length)
        {
            return new LeftFunction(text, length);
        }

        public static LeftFunction Left(StringExpression text, IntegerExpression length)
        {
            return new LeftFunction(text, length);
        }

        public static RightFunction Right(IStringExpression text, IntegerExpression length)
        {
            return new RightFunction(text, length);
        }

        public static RightFunction Right(StringExpression text, IntegerExpression length)
        {
            return new RightFunction(text, length);
        }

        public static ConcatFunction Concat(IStringExpression text, params IStringExpression[] texts)
        {
            var lst = texts.ToList();
            lst.Insert(0, text);
            return new ConcatFunction(lst);
        }

        public static ConcatFunction Concat(StringExpression text, params StringExpression[] texts)
        {
            var lst = texts.ToList<IStringExpression>();
            lst.Insert(0, text);
            return new ConcatFunction(lst);
        }

        public static NvlFunction Nvl(IExpression value, ConstantExpression then, ConstantExpression @default)
        {
            return new NvlFunction(value, then, @default);
        }

        public static NvlFunction Nvl(IExpression value, IExpression @default)
        {
            return new NvlFunction(value, @default);
        }

        public static NvlFunction Nvl(IExpression value, StringExpression @default)
        {
            return new NvlFunction(value, @default);
        }

        public static NvlFunction Nvl(StringExpression value, StringExpression @default)
        {
            return new NvlFunction(value, @default);
        }

        public static NvlFunction Nvl(StringExpression value, StringExpression then, StringExpression @default)
        {
            return new NvlFunction(value, then, @default);
        }

        public static LogicalExpression And(IPredicateExpression left, IPredicateExpression right)
        {
            return new LogicalExpression(left, right, LogicalOperator.And);
        }

        public static LogicalExpression Or(IPredicateExpression left, IPredicateExpression right)
        {
            return new LogicalExpression(left, right, LogicalOperator.Or);
        }

        public static LikeExpression StartWith(IExpression value, string pattern)
        {
            return new LikeExpression(value, $"{pattern}%");
        }

        public static LikeExpression EndWith(IExpression value, string pattern)
        {
            return new LikeExpression(value, $"%{pattern}");
        }

        public static LikeExpression Contains(IExpression value, string pattern)
        {
            return new LikeExpression(value, $"%{pattern}%");
        }

        public static LikeExpression Like(IExpression value, StringExpression pattern)
        {
            return new LikeExpression(value, pattern);
        }

        public static DiffFunction YearDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Year);
        }

        public static DiffFunction MonthDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Month);
        }

        public static DiffFunction DayDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Day);
        }

        public static DiffFunction WeekDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Week);
        }

        public static DiffFunction HourDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Hour);
        }

        public static DiffFunction MinuteDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Minute);
        }

        public static DiffFunction SecondDiff(IDateTimeExpression start, IDateTimeExpression end)
        {
            return new DiffFunction(start, end, ExtractType.Second);
        }

        public static NotExpression Not(IPredicateExpression expression) => new NotExpression(expression);

        #region [Comparision]

        public static ComparisonExpression Compare(IExpression left, IExpression right,
            ComparisonOperator @operator)
        {
            return new ComparisonExpression(left, right, @operator);
        }

        public static ComparisonExpression Equal(ConstantExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.Equal);
        }

        public static ComparisonExpression Equal(IExpression left, ConstantExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.Equal);
        }

        public static ComparisonExpression Equal(IExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.Equal);
        }

        public static ComparisonExpression Less(IExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.Less);
        }

        public static ComparisonExpression Greater(IExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.Greater);
        }

        public static ComparisonExpression LessOrEqual(IExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.LessOrEqual);
        }

        public static ComparisonExpression GreaterOrEqual(IExpression left, IExpression right)
        {
            return new ComparisonExpression(left, right, ComparisonOperator.GreaterOrEqual);
        }

        #endregion

        public static CoalesceFunction Coalesce(params IExpression[] expressions)
        {
            return new CoalesceFunction(expressions);
        }

        public static DecodeFunction Decode(params IExpression[] expressions)
        {
            return new DecodeFunction(expressions);
        }

        public static IntersectionStatement Intersect(IDataTableStatement left, IDataTableStatement right)
        {
            return new IntersectionStatement(left, right);
        }

        public static MinusStatement Minus(IDataTableStatement left, IDataTableStatement right)
        {
            return new MinusStatement(left, right);
        }


        public static UnionStatement Union(IDataTableStatement left, IDataTableStatement right,
            UnionType type = UnionType.Distinct)
        {
            return new UnionStatement(left, right, type);
        }

        public static UnionStatement UnionAll(IDataTableStatement left, IDataTableStatement right)
        {
            return new UnionStatement(left, right, UnionType.All);
        }

        public static ExistsExpression Exists(ISubquery statement)
        {
            return new ExistsExpression(statement);
        }

        public static T Tag<T>(this T self, object tag) where T : ISqlObject
        {
            self.Tag = tag;
            return self;
        }

        #region [Select]

        public static IKeySelect Select()
        {
            return SqlBuilder.Select();
        }

        public static IKeySelect Select<T>(T element) where T : ISelectElement
        {
            return SqlBuilder.Select(Wrap(element));
        }

        public static IKeySelect Select<T1, T2>(T1 element1, T2 element2)
            where T1 : ISelectElement
            where T2 : ISelectElement
        {
            return SqlBuilder.Select(Wrap(element1), Wrap(element2));
        }

        public static IKeySelect Select<T1, T2, T3>(T1 element1, T2 element2, T3 element3)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4>(T1 element1, T2 element2, T3 element3, T4 element4)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5>(T1 element1, T2 element2, T3 element3, T4 element4,
            T5 element5)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6>(T1 element1, T2 element2, T3 element3, T4 element4,
            T5 element5, T6 element6)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7>(T1 element1, T2 element2, T3 element3, T4 element4,
            T5 element5, T6 element6, T7 element7)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8>(T1 element1, T2 element2, T3 element3,
            T4 element4, T5 element5, T6 element6, T7 element7, T8 element8)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 element1, T2 element2, T3 element3,
            T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 element1, T2 element2, T3 element3,
            T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9, T10 element10)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(T1 element1, T2 element2,
            T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9, T10 element10,
            T11 element11)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(T1 element1, T2 element2,
            T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9, T10 element10,
            T11 element11, T12 element12)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
            where T12 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11),
                Wrap(element12)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(T1 element1,
            T2 element2, T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9,
            T10 element10, T11 element11, T12 element12, T13 element13)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
            where T12 : ISelectElement
            where T13 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11),
                Wrap(element12),
                Wrap(element13)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(T1 element1,
            T2 element2, T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9,
            T10 element10, T11 element11, T12 element12, T13 element13, T14 element14)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
            where T12 : ISelectElement
            where T13 : ISelectElement
            where T14 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11),
                Wrap(element12),
                Wrap(element13),
                Wrap(element14)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(T1 element1,
            T2 element2, T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8, T9 element9,
            T10 element10, T11 element11, T12 element12, T13 element13, T14 element14, T15 element15)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
            where T12 : ISelectElement
            where T13 : ISelectElement
            where T14 : ISelectElement
            where T15 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11),
                Wrap(element12),
                Wrap(element13),
                Wrap(element14),
                Wrap(element15)
            );
        }

        public static IKeySelect Select<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
            T1 element1, T2 element2, T3 element3, T4 element4, T5 element5, T6 element6, T7 element7, T8 element8,
            T9 element9, T10 element10, T11 element11, T12 element12, T13 element13, T14 element14, T15 element15,
            T16 element16)
            where T1 : ISelectElement
            where T2 : ISelectElement
            where T3 : ISelectElement
            where T4 : ISelectElement
            where T5 : ISelectElement
            where T6 : ISelectElement
            where T7 : ISelectElement
            where T8 : ISelectElement
            where T9 : ISelectElement
            where T10 : ISelectElement
            where T11 : ISelectElement
            where T12 : ISelectElement
            where T13 : ISelectElement
            where T14 : ISelectElement
            where T15 : ISelectElement
            where T16 : ISelectElement
        {
            return SqlBuilder.Select(
                Wrap(element1),
                Wrap(element2),
                Wrap(element3),
                Wrap(element4),
                Wrap(element5),
                Wrap(element6),
                Wrap(element7),
                Wrap(element8),
                Wrap(element9),
                Wrap(element10),
                Wrap(element11),
                Wrap(element12),
                Wrap(element13),
                Wrap(element14),
                Wrap(element15),
                Wrap(element16)
            );
        }

        private static SelectElement Wrap(ISelectElement element)
        {
            if (element is SelectElement selectElement) return selectElement;

            return new SelectElement((IExpression) element);
        }

        #endregion
    }
}